package cli

import (
	"fmt"
	"regexp"
	"sync"

	"golang.org/x/tools/go/packages"
)

/*
*
代码生成器主类
*/
type SpringBootCli struct {
	//加载时需要忽略包名字
	ignorePkgs   []string
	ignorePkgsRe []*regexp.Regexp // 编译后的正则
	//扫描的根路径，默认是当前项目下所有，包括依赖包:"./..."
	rootPattern  string
	ignoreStdLib bool // 新增：是否忽略 Go 标准库
	//已经加载的数据包信息
	pkgs []*packages.Package
	//元信息存储map
	meta      map[string]interface{}
	scanMates []ScanMate
}

// 注册ScanMate
func (c *SpringBootCli) RegisterScanMate(scanMate ScanMate) {
	c.scanMates = append(c.scanMates, scanMate)
}

// 开始生成代码
func (c *SpringBootCli) Scan() {
	//1.首先去加载数据包
	c.SetIgnoreStdLib(true)
	c.CompileIgnorePkgs()
	c.LoadAllPackages()
	//2.开始解析元数据
	wg := sync.WaitGroup{}
	for _, scanMate := range c.scanMates {
		wg.Add(1)
		go func() {
			scanMate.ParseMate(c.pkgs, c.meta)
			wg.Done()
		}()
	}
	//等待所有解析完成
	wg.Wait()
	//开始生成静态代码

	//开始生成初始化代码

}

// 设置忽略包（正则模式）
func (c *SpringBootCli) SetIgnorePkgs(pkgs []string) {
	c.ignorePkgs = append(c.ignorePkgs, pkgs...)
}

func (c *SpringBootCli) CompileIgnorePkgs() {
	c.ignorePkgsRe = make([]*regexp.Regexp, 0, len(c.ignorePkgs))
	for _, pattern := range c.ignorePkgs {
		re, err := regexp.Compile(pattern)
		if err != nil {
			panic(fmt.Errorf("ignorePkgs 正则编译失败: %w", err))
		}
		c.ignorePkgsRe = append(c.ignorePkgsRe, re)
	}
}

func (c *SpringBootCli) SetRootPattern(rootPattern string) {
	c.rootPattern = rootPattern
}

// 开启或关闭忽略标准库
func (c *SpringBootCli) SetIgnoreStdLib(ignore bool) {
	c.ignoreStdLib = ignore
	if ignore {
		c.ignorePkgs = append(c.ignorePkgs, getStdLibList()...)
	}
}

/*
*
加载数据包
*/
func (c *SpringBootCli) LoadPackages() {
	cfg := &packages.Config{
		Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax |
			packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports,
	}

	// 如果 ignorePkgs 不为空，则构建新的 patterns
	var patterns []string
	if len(c.ignorePkgs) > 0 {
		// 这里假设 rootPattern 常常是 "./..." 表示当前工程所有包，
		// 我们需要枚举出所有符合条件的包，然后排除 ignorePkgs 中的
		allPkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, c.rootPattern)
		if err != nil {
			panic(fmt.Errorf("扫描包失败: %w", err))
		}
		for _, pkg := range allPkgs {
			ignore := false
			for _, ignoreName := range c.ignorePkgs {
				if pkg.Name == ignoreName {
					ignore = true
					break
				}
			}
			if !ignore {
				patterns = append(patterns, pkg.PkgPath)
			}
		}
	} else {
		// 直接用 rootPattern
		patterns = []string{c.rootPattern}
	}

	pkgs, err := packages.Load(cfg, patterns...)
	if err != nil {
		panic(fmt.Errorf("加载包失败: %w", err))
	}

	c.pkgs = pkgs
}

/*
*

	加载所有包(当前机器依赖)
*/
func (c *SpringBootCli) LoadAllPackages() {
	cfg := &packages.Config{
		Mode: packages.NeedName | packages.NeedFiles |
			packages.NeedSyntax | packages.NeedTypes |
			packages.NeedTypesInfo | packages.NeedImports,
	}

	if c.rootPattern == "" {
		c.rootPattern = "."
	}

	roots, err := packages.Load(cfg, c.rootPattern)
	if err != nil {
		panic(fmt.Errorf("加载包失败: %w", err))
	}

	visited := make(map[string]*packages.Package)

	var collect func(pkg *packages.Package)
	collect = func(pkg *packages.Package) {
		if pkg == nil {
			return
		}
		if _, ok := visited[pkg.PkgPath]; ok {
			return
		}
		if c.shouldIgnore(pkg) {
			return
		}
		visited[pkg.PkgPath] = pkg
		for _, dep := range pkg.Imports {
			collect(dep)
		}
	}

	for _, root := range roots {
		collect(root)
	}

	for _, p := range visited {
		c.pkgs = append(c.pkgs, p)
	}
}

func (c *SpringBootCli) PrintLoadedPkgs() {
	for _, pkg := range c.pkgs {
		fmt.Println(pkg)
	}
}

// 打印全部bean信息
func (c *SpringBootCli) PrintBeanMeta() {
	beans := c.meta["beans"].([]BeanDefinition)
	for _, bean := range beans {
		fmt.Println(bean)
	}
}

// 判断是否需要忽略包
func (c *SpringBootCli) shouldIgnore(pkg *packages.Package) bool {
	for _, re := range c.ignorePkgsRe {
		// 既匹配 pkg.Name 也匹配 pkg.PkgPath
		if re.MatchString(pkg.Name) || re.MatchString(pkg.PkgPath) {
			return true
		}
	}
	return false
}
